package com.legendframework.core.ioc;

import com.legendframework.core.Store;
import com.legendframework.core.aop.AbstractProxy;
import com.legendframework.core.ioc.annotation.Bean;
import com.legendframework.core.ioc.annotation.Resource;
import com.legendframework.core.ioc.exception.BeansException;
import com.legendframework.core.ioc.exception.NoSuchBeanDefinitionException;
import com.legendframework.core.ioc.pojo.GenerateBeanParam;
import com.legendframework.core.AbstractLegendPlugin;
import com.legendframework.core.dao.Return;
import com.legendframework.core.dao.support.ClassUtils;
import com.legendframework.core.support.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public abstract class CommonBeansGenerate extends AbstractBeansGenerate {

    /**
     * 切面实现
     */
    private Function aop;

    public CommonBeansGenerate(Store store) {
        super(store);
        AbstractProxy proxyAop = getProxyAop();
        aop = proxyAop == null ? a -> a : proxyAop;
    }

    @Override
    protected Return<String, Object> newInstance(Class<?> cls , AbstractLegendPlugin plugin) {
        try {
            //获取Bean名称
            String beanName = getBeanName(cls);
            //实例化
            return Return.build(beanName,injectionBean(beanName,cls.newInstance(),plugin));
        } catch (Throwable e) {
            throw new BeansException("构建Bean: ["+cls.getName()+"] 失败, 原因：'"+ e.getMessage()+"'",e);
        }
    }

    /**
     * 对这个Bean内部的字段进行注入
     *
     * @param beanName     对象名称
     * @param bean     对象
     * @return
     */
    protected Object injectionBean(String beanName , Object bean , AbstractLegendPlugin plugin) {
        Map<String, Object> createdBeans = store.getBeansFactory().getCreatedBeans();
        //将bean放入创建中容器
        createdBeans.put(beanName,bean);
        new Thread(() -> {
            try {
                //获取该对象所有字段(包括父类)
                List<Field> fields = ClassUtils.getByClass(bean.getClass());
                //注入需要注入的字段
                for (Field field : fields) {
                    field.setAccessible(true);
                    if (field.get(bean) != null) {
                        continue;
                    }
                    if (field.isAnnotationPresent(Resource.class)) {
                        Resource annotation = field.getAnnotation(Resource.class);
                        try {
                            Return<String, Object> beanReturn = getBean(annotation.name(), field.getType());
                            //设置字段的值
                            field.setAccessible(true);
                            field.set(bean, beanReturn.getR2());
                        } catch (NoSuchBeanDefinitionException e) {
                            //没有找到Bean异常,获取这个没有的Bean
                            Object fieldBean = getBeanByUndeterminedType(field.getType(), annotation , plugin);
                            //设置字段的值
                            field.set(bean, fieldBean);
                        } catch (BeansException e) {
                            if (!annotation.required()) {
                                continue;
                            }
                            throw e;
                        }
                    }
                }
            } catch (Throwable e) {
                throw new BeansException("注入Bean: ["+bean.getClass().getName()+"] 的所需字段失败, 原因：'"+ e.getMessage()+"'",e);
            } finally {
                //将对象移除创建中容器
                createdBeans.remove(beanName);
            }
        }).start();
        return bean;
    }

    /**
     * 将这个Method的返回值作为对象返回
     * @param method 方法
     * @param obj 执行方法的对象
     * @return 方法的执行结果
     */
    @Override
    protected Return<String, Object> generateMethod(Method method , Object obj , AbstractLegendPlugin plugin){
        try {
            if (!method.isAnnotationPresent(Bean.class)) {
                return null;
            }
            Bean annotation = method.getAnnotation(Bean.class);
            //获取方法的入参
            List<Object> paramsBean = new ArrayList<>();
            for (Parameter parameter : method.getParameters()) {
                if (!parameter.isAnnotationPresent(Resource.class)) {
                    continue;
                }
                try {
                    Return<String, Object> bean = getBean(null, parameter.getType());
                    //设置字段的值
                    paramsBean.add(bean.getR2());
                } catch (NoSuchBeanDefinitionException e) {
                    //没有找到Bean异常,获取这个没有的Bean
                    Object fieldBean = getBeanByUndeterminedType(parameter.getType(),parameter.getAnnotation(Resource.class),plugin);
                    //设置字段的值
                    paramsBean.add(fieldBean);
                }
            }
            Object result;
            //执行方法
            if (Modifier.isStatic(method.getModifiers())) {
                result = method.invoke(method.getDeclaringClass(), paramsBean.toArray());
            }else {
                result = method.invoke(obj, paramsBean.toArray());
            }
            return Return.build(StringUtils.isEmpty(annotation.value()) ? result.getClass().getName() : annotation.value() ,result);
        } catch (Throwable e) {
            throw new BeansException("构建方法返回值Bean: ["+method+"] 失败, 原因：'"+ e.getMessage()+"'",e);
        }
    }

    @Override
    protected Return<String, Object> getBean(String beanName, Class<?> cls) {
        return (Return<String, Object>) store.getBeansFactory().getBeanAndName(beanName, cls);
    }

    @Override
    protected Object getBeanByUndeterminedType(Class<?> cls, Resource resource , AbstractLegendPlugin plugin) {
        GenerateBeanParam generateBeanParam = store.getBeansFactory().getBeanTypeByClass(cls, resource, plugin);
        if (generateBeanParam == null) {
            return null;
        }
        Object bean;
        if (generateBeanParam.getMethod() == null) {
            bean = store.getBeansFactoryManager().generate(generateBeanParam.getCls(),generateBeanParam.getPlugin());
        }else {
            bean = generateMethod(generateBeanParam.getMethod(),generateBeanParam.getObject(),generateBeanParam.getPlugin()).getR2();
        }
        return bean;
    }

    @Override
    public void successHandle(String beanName, Object bean , AbstractLegendPlugin plugin) {

    }

    @Override
    protected <T> T aop(T obj) {
        return (T) aop.apply(obj);
    }

    /**
     * 切面实现类
     *
     * 仅需实现相应的通知方法
     * @return
     */
    protected AbstractProxy getProxyAop(){
        return new AbstractProxy() {
            @Override
            public void enhance(Object source, Method method, Object[] args) {

            }

            @Override
            public Object postposition(Object source, Method method, Object[] args, Object result) {
                return result;
            }

            @Override
            public Object exceptionHandle(Object source, Method method, Object[] args, Throwable e) throws Throwable {
                throw e;
            }
        };
    }
}
